# -*-coding:utf-8-*-

from bson.objectid import ObjectId
from daos.base_async import BaseDao, BaseRecord
import pymongo
from tornado.gen import coroutine, Return

Roles_Document_Structure = {
    '_id': 'ROLE_ID',  # user_id
    'uid': 'UID',

    'name': 'NAME',
    'superior_id': 'SUPERIOR_ID',  # 上级角色标识，若有，判断其有效性
    'privileges': {
        'roles': 0b1111,  # CRUD, 目前限定只有 root 用户有 roles 的 C_UD 权限
        'products': 0b1010,
    },
    'grade': 1,  # 同级别角色的等级
}


class Role(BaseRecord):
    pass


class RolesDao(BaseDao):
    def __init__(self, db):
        super(RolesDao, self).__init__(db, 'roles')
        # self.collection.create_index([('name', pymongo.ASCENDING)], unique=True)
        # self.collection.drop_index('name_1')

    def _get_record_object(self, data):
        return Role(data)

    @coroutine
    def get_role_list_by_ids(self, rids):
        result = []
        # TODO: tmp approach
        import settings
        root_sub_roles_keys = settings.root_role_subordinates.keys()
        for rid in rids:
            if rid in root_sub_roles_keys:
                r = settings.root_role_subordinates[rid]
                result.append({'role_id': r['role_id'], 'name': r['name']})
            if rid == settings.root_role_id:
                result.append(settings.root_role)
        filter_cond = {'_id': {'$in': [ObjectId(x) for x in rids]}}
        cursor = self.find(filter_cond, {'name': 1})
        while (yield cursor.fetch_next):
            r = cursor.next_object()
            r['role_id'] = str(r.pop('_id'))
            result.append(r)
        raise Return(result)

    @coroutine
    def create_role(self, uid, data):
        data = self.extract_fields(data, required_fields=['name'], optional_fields=['superior_id', 'grade'])
        data.update(uid=uid)
        result = yield self.insert_one(data)
        raise Return({'role_id': str(result.inserted_id)})

    @coroutine
    def get_role(self, uid, rid, force_owner=True):
        # TODO: tmp approach
        import settings
        root_sub_roles_keys = settings.root_role_subordinates.keys()
        for x in root_sub_roles_keys:
            if x == rid:
                r = settings.root_role_subordinates[x]
                result = {'role_id': r['role_id'], 'name': r['name']}
                raise Return(result)

        filter_cond = {'_id': ObjectId(rid)}
        if force_owner:
            filter_cond.update(uid=uid)
        result = yield self.find_one(filter_cond)
        result['role_id'] = str(result.pop('_id'))
        raise Return(result)

    @coroutine
    def get_role_subordinates(self, uid, rid, force_owner=True):
        # TODO: tmp approach
        import settings
        if uid == settings.root_user_id:
            raise Return(settings.root_role_subordinates.values())

        filter_cond = {'superior_id': rid}
        if force_owner:
            filter_cond.update(uid=uid)
        projection = {'superior_id': 0, 'uid': 0}
        result = []
        cursor = self.find(filter_cond, projection)
        while (yield cursor.fetch_next):
            a = cursor.next_object()
            a['role_id'] = str(a.pop('_id'))
            result.append(a)
        raise Return(result)

    @coroutine
    def get_role_siblings(self, uid, rid):
        role = yield self.get_role(uid, rid, force_owner=False)
        if 'superior_id' not in role:
            raise Return([])
        subordinates = yield self.get_role_subordinates(uid, role['superior_id'], force_owner=False)
        result = []
        for r in subordinates:
            if r['role_id'] == rid:
                continue
            # 把高级别的角色去掉
            if 'grade' in role and 'grade' in r and role['grade'] < r['grade']:
                continue
            result.append(r)
        raise Return(result)

    @coroutine
    def update_role(self, uid, rid, data):
        # NOTE: 注意目前有些代码逻辑依赖角色名
        data = self.extract_fields(data, optional_fields=['grade'])
        filter_cond = {'uid': uid, '_id': ObjectId(rid)}
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)

    @coroutine
    def delete_role(self, uid, rid):
        filter_cond = {'uid': uid, '_id': ObjectId(rid)}
        result = yield self.delete_one(filter_cond)
        raise Return(1 == result.deleted_count)
